#include <jni.h>
#include <string>
#include <pthread.h>

#include "x264.h"
#include "librtmp/rtmp.h"
#include "VideoChannel.h"
#include "macro.h"
#include "safe_queue.h"
#include "AudioChannel.h"

//为了防止用户重复点击开始直播，导致重新初始化
int isStart = 0;
VideoChannel *videoChannle;
AudioChannel *audioChannel;
pthread_t pid;
uint32_t start_time;
int readyPushing = 0;
//队列
SafeQueue<RTMPPacket *> packets;

/**
 * 封装rtmp数据之后的回调
 */
void callback(RTMPPacket *packet) {
    if (packet) {
        //设置时间戳
        packet->m_nTimeStamp = RTMP_GetTime() - start_time;
        //加入队列
        packets.put(packet);
    }
}

void releasePackets(RTMPPacket *&packet) {
    if (packet) {
        RTMPPacket_Free(packet);
        delete packet;
        packet = 0;
    }
}

/**
 * 线程回调
 */
void *start(void *args) {
    char *url = static_cast<char *>(args);

    RTMP *rtmp = 0;
    //1 rtmp申请内存
    rtmp = RTMP_Alloc();
    if (!rtmp) {
        LOGE("alloc RTMP 初始化失败");
        return NULL;
    }
    //2 初始化
    RTMP_Init(rtmp);
    //3 设置地址
    int ret = RTMP_SetupURL(rtmp, url);
    if (!ret) {
        LOGE("设置地址失败：%s", url);
        return NULL;
    }

    //设置链接时间
    rtmp->Link.timeout = 5;
    //4.开启输出模式
    RTMP_EnableWrite(rtmp);
    //5.连接服务器
    ret = RTMP_Connect(rtmp, 0);
    if (!ret) {
        LOGE("连接服务器失败：%s", url);
        return NULL;
    }
    //6.连接流
    ret = RTMP_ConnectStream(rtmp, 0);
    if (!ret) {
        LOGE("连接流失败：%s", url);
        return NULL;
    }

    start_time = RTMP_GetTime();
    //表示可以推流了
    readyPushing = 1;
    packets.setWork(1);
    RTMPPacket *packet = 0;
    callback(audioChannel->getAudioTag());
    while (readyPushing) {
        packets.get(packet);
        LOGE("取一帧数据");
        if (!readyPushing) {
            break;
        }
        if (!packet) {
            continue;
        }
        //设置流类型
        packet->m_nInfoField2 = rtmp->m_stream_id;

        //7.发送数据包
        ret = RTMP_SendPacket(rtmp, packet, 1);

        //8.释放packet
        releasePackets(packet);
    }

    //9.关闭
    isStart = 0;
    readyPushing = 0;
    packets.setWork(0);
    packets.clear();
    if (rtmp) {
        RTMP_Close(rtmp);
        RTMP_Free(rtmp);
    }
    delete (url);
    return 0;
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_whg_push_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "WhgPush";
    return env->NewStringUTF(hello.c_str());
}


extern "C"
JNIEXPORT void JNICALL
Java_com_whg_push_LivePusher_native_1init(JNIEnv *env, jobject thiz) {
    videoChannle = new VideoChannel;
    videoChannle->setVideoCallback(callback);

    audioChannel = new AudioChannel;
    audioChannel->setAudioCallback(callback);
}

/**
 * 宽高改变
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_whg_push_LivePusher_native_1setVideoEncInfo(JNIEnv *env, jobject thiz, jint height,
                                                     jint width, jint fps, jint bitrate) {

    if (!videoChannle) {
        return;
    }
    //处理视频
    videoChannle->setVideoEncInfo(width, height, fps, bitrate);
}


extern "C"
JNIEXPORT void JNICALL
Java_com_whg_push_LivePusher_native_1start(JNIEnv *env, jobject thiz, jstring path_) {
    const char *path = env->GetStringUTFChars(path_, 0);
    if (isStart) {
        return;
    }
    isStart = 1;

    char *url = new char[strlen(path) + 1];
    strcpy(url, path);

    pthread_create(&pid, 0, start, url);

    env->ReleaseStringUTFChars(path_, path);
}

/**
 * 推流
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_whg_push_LivePusher_native_1pushVideo(JNIEnv *env, jobject thiz, jbyteArray data_) {

    if (!videoChannle || !readyPushing) {
        return;
    }
    jbyte *data = env->GetByteArrayElements(data_, NULL);

    //解码
    videoChannle->encodeData(data);

    env->ReleaseByteArrayElements(data_, data, 0);
}


extern "C"
JNIEXPORT void JNICALL
Java_com_whg_push_LivePusher_native_1pushAudio(JNIEnv *env, jobject thiz, jbyteArray bytes_) {
    //pcm
    jbyte *data = env->GetByteArrayElements(bytes_, NULL);

    if (!audioChannel || !readyPushing) {
        return;
    }

    audioChannel->encodeData(data);

    env->ReleaseByteArrayElements(bytes_, data, 0);

}


extern "C"
JNIEXPORT void JNICALL
Java_com_whg_push_LivePusher_native_1setAudioEncInfo(JNIEnv *env, jobject thiz, jint samples_in_hz,
                                                     jint channels) {

    if (audioChannel) {
        audioChannel->setAudioEncInfo(samples_in_hz, channels);
    }

}

extern "C"
JNIEXPORT jint JNICALL
Java_com_whg_push_LivePusher_getInputSamples(JNIEnv *env, jobject thiz) {
    if (audioChannel) {
        return audioChannel->getInputSamples();
    }
    return -1;

}